package cn.angis.system.controller;

import cn.angis.common.annotation.SLog;
import cn.angis.common.api.ApiErrorCode;
import cn.angis.common.constant.Constant;
import cn.angis.common.model.R;
import cn.angis.db.controller.BaseController;
import cn.angis.system.dto.input.LoginInput;
import cn.angis.system.dto.output.LoginOutput;
import cn.angis.system.dtomapper.UserStruct;
import cn.angis.system.model.Dept;
import cn.angis.system.model.Role;
import cn.angis.system.model.User;
import cn.angis.system.service.DeptService;
import cn.angis.system.service.RoleService;
import cn.angis.system.service.RoleresService;
import cn.angis.system.service.UserService;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import org.noear.solon.annotation.*;
import org.noear.solon.data.annotation.Cache;
import org.noear.solon.validation.annotation.Validated;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 登陆控制器
 *
 * @author angis.cn
 * @Date 2023/1/6 18:56
 */
@Controller
public class LoginController extends BaseController<UserService, User> {

    @Inject
    private DeptService deptService;
    @Inject
    private RoleresService roleresService;
    @Inject
    private RoleService roleService;
    @Inject
    private SM2 sm2;

    /**
     * 用户登陆
     * * @param loginInput
     * @return: R<LoginOutput>
     * @throws: 
     * @Date: 2023/1/7     
     */
    @Post
    @SLog("登陆")
    @Mapping("login")
    public R<LoginOutput> login(@Validated LoginInput loginInput) {
        loginInput.setPassword(StrUtil.utf8Str(sm2.decrypt(loginInput.getPassword(), KeyType.PrivateKey)));

        Thread.currentThread().setName(loginInput.getUsername());
        User user = baseService.getByUserName(loginInput.getUsername());
        if (user == null) {
            saveLoginTimes(user.getUsername());
            return R.error(ApiErrorCode.USER_PASSWORD_ERROR);
        }
        if (Constant.DISABLE.equals(user.getStatus()) || StpUtil.isDisable(user.getUsername())) {
            return R.error(ApiErrorCode.USER_LOCKED_ERROR);
        }
        if (!user.getPassword().equals(SmUtil.sm3(loginInput.getPassword()))) {
            saveLoginTimes(user.getUsername());
            return R.error(ApiErrorCode.USER_PASSWORD_ERROR);
        }
        LoginOutput loginOutput = UserStruct.INSTANCE.toLoginOutPut(user);
        // 设置权限
        loginOutput.setResources(roleresService.getRoleAndResourceByUserId(user.getId()));
        if (StrUtil.isNotBlank(user.getDeptId())) {
            loginOutput.setDeptName(deptService.getById(new Dept(user.getDeptId())).getName());
        }
        List<Role> roleList = roleService.getRoleListByUserId(user.getId());
        if (CollUtil.isNotEmpty(roleList)) {
            loginOutput.setRoleName(CollUtil.join(roleList.stream().map(Role::getName).collect(Collectors.toList()), ","));
        }
        // sa-token 登录
        StpUtil.login(user.getId());
        // 获取token
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        // 获取session
        SaSession session = StpUtil.getSession();
        session.set(Constant.SESSION_USER_NAME, loginOutput.getUsername());
        // 设置用户信息
        session.set(Constant.SESSION_USER_KEY, loginOutput);
        loginOutput.setToken(tokenInfo.getTokenValue());
        return success(loginOutput);
    }

    private void saveLoginTimes(String userAccount) {
        String loginErrorKey = Constant.SESSION_USER_NAME + userAccount;
        String strnum = SaManager.getSaTokenDao().get(loginErrorKey);
        Integer number = strnum==null?null:Integer.valueOf(strnum);
        if (number == null) {
            // 如果redis中没有保存，代表失败第一次
            number = 1;
        } else if (number < 5) {
            number++;
        }
        SaManager.getSaTokenDao().set(loginErrorKey, number.toString(), Constant.USER_LOCK_TIME);
        if (number <5 ) return;
        // 第五次封禁账号,第六次进入isDisableTime方法，返回用户还需等待时间
        StpUtil.disable(userAccount, Constant.USER_LOCK_TIME);
        // 删除redis 中的key
        clearLoginErrorTimes(userAccount);
    }

    private void clearLoginErrorTimes(String userAccount) {
        String loginErrorKey = Constant.SESSION_USER_NAME + userAccount;
        SaManager.getSaTokenDao().delete(loginErrorKey);
    }

    /**
     * 获取用户信息
     * * @param 
     * @return: R<LoginOutput>
     * @throws: 
     * @Date: 2023/1/7     
     */
    @Get
    @SLog
    @Mapping("getUser")
    public R<LoginOutput> getUser() {
        SaSession session = StpUtil.getSession();
        LoginOutput loginOutputDTO = session.getModel(Constant.SESSION_USER_KEY, LoginOutput.class);
        return success(loginOutputDTO);
    }

    /**
     * 用户退出
     * * @param
     * @return: R<String>
     * @throws:
     * @Date: 2023/1/7
     */
    @Post
    @SLog
    @Mapping("logout")
    public R<String> logout() {
        StpUtil.logout();
        return success("退出成功");
    }
}
